#include "Pin.h"
#include "Enumerations.h"

Pin::Pin() { }

Pin::Pin(Enums::Gpio::Ports port, Enums::Gpio::Pins::Number number)
{
	this->SetPort(port);
	this->Number = number;
	this->SetDirection(Enums::Gpio::Pins::Direction::Input);
}

Pin::Pin(Enums::Gpio::Ports port, Enums::Gpio::Pins::Number number, Enums::Gpio::Pins::Direction direction)
{
	this->SetPort(port);
	this->Number = number;
	this->SetDirection(direction);
}

Pin::~Pin() { }

void Pin::SetPort(Enums::Gpio::Ports port)
{
	this->Port = port;
}

void Pin::SetDirection(Enums::Gpio::Pins::Direction direction)
{
	this->Direction = direction;

	// Sets the direction of the pin as an input (1) or output (0)
	switch (this->Port)
	{
		case Enums::Gpio::Ports::PortA:
			TRISA = Direction == Enums::Gpio::Pins::Direction::Input ?
					TRISA | (1 << (UINT16)this->Number) :
					TRISA & ~(1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortB:
			TRISB = Direction == Enums::Gpio::Pins::Direction::Input ?
					TRISB | (1 << (UINT16)this->Number) :
					TRISB & ~(1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortC:
			TRISC = Direction == Enums::Gpio::Pins::Direction::Input ?
					TRISC | (1 << (UINT16)this->Number) :
					TRISC & ~(1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortD:
			TRISD = Direction == Enums::Gpio::Pins::Direction::Input ?
					TRISD | (1 << (UINT16)this->Number) :
					TRISD & ~(1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortE:
			TRISE = Direction == Enums::Gpio::Pins::Direction::Input ?
					TRISE | (1 << (UINT16)this->Number) :
					TRISE & ~(1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortF:
			TRISF = Direction == Enums::Gpio::Pins::Direction::Input ?
					TRISF | (1 << (UINT16)this->Number) :
					TRISF & ~(1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortG:
			TRISG = Direction == Enums::Gpio::Pins::Direction::Input ?
					TRISG | (1 << (UINT16)this->Number) :
					TRISG & ~(1 << (UINT16)this->Number);
			break;
	}

}

void Pin::SetPin()
{
	// Sets the appropriate latch (LATx) bit for the pin
	switch (this->Port)
	{
		case Enums::Gpio::Ports::PortA:
			LATA |= (1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortB:
			LATB |= (1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortC:
			LATC |= (1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortD:
			LATD |= (1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortE:
			LATE |= (1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortF:
			LATF |= (1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortG:
			LATG |= (1 << (UINT16)this->Number);
			break;
	}
}

void Pin::ClearPin()
{
	// Clears the appropriate latch (LATx) bit for the pin
	switch (this->Port)
	{
		case Enums::Gpio::Ports::PortA:
			LATA &= ~(1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortB:
			LATB &= ~(1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortC:
			LATC &= ~(1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortD:
			LATD &= ~(1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortE:
			LATE &= ~(1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortF:
			LATF &= ~(1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortG:
			LATG &= ~(1 << (UINT16)this->Number);
			break;
	}
}

void Pin::TogglePin()
{
	// Toggles the appropriate latch (LATx) bit for the pin
	switch (this->Port)
	{
		case Enums::Gpio::Ports::PortA:
			LATA ^= (1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortB:
			LATB ^= (1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortC:
			LATC ^= (1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortD:
			LATD ^= (1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortE:
			LATE ^= (1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortF:
			LATF ^= (1 << (UINT16)this->Number);
			break;
		case Enums::Gpio::Ports::PortG:
			LATG ^= (1 << (UINT16)this->Number);
			break;
	}
}

Enums::Gpio::Pins::Value Pin::GetPinValue()
{
	UINT16 PortState;

	// Read the entire PORTx
	switch (this->Port)
	{
		case Enums::Gpio::Ports::PortA:
			PortState = PORTA;
			break;
		case Enums::Gpio::Ports::PortB:
			PortState = PORTB;
			break;
		case Enums::Gpio::Ports::PortC:
			PortState = PORTC;
			break;
		case Enums::Gpio::Ports::PortD:
			PortState = PORTD;
			break;
		case Enums::Gpio::Ports::PortE:
			PortState = PORTE;
			break;
		case Enums::Gpio::Ports::PortF:
			PortState = PORTF;
			break;
		case Enums::Gpio::Ports::PortG:
			PortState = PORTG;
			break;
	}

	// Return HIGH if bit is set or LOW if it is not
	return (PortState & (UINT16) (1 << (UINT16)this->Number)) == 0 ? Enums::Gpio::Pins::Value::High : Enums::Gpio::Pins::Value::Low;
}